package com.xiaozheng.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @Package: com.xiaozheng.proxy
 * @ClassName: Client
 * @Author: 小政同学    QQ:xiaozheng666888@qq.com
 * @CreateTime: 2020/9/27 17:20
 * @What_is_this_file_for: 模拟一个消费者
 * @Description: 描述
 */
public class Client {
    public static void main(String[] args) {
        final ProducerImpl producerImpl = new ProducerImpl();
        /**
         * 动态代理：
         * 	特点：字节码随用随加载，随用随创建
         * 	作用：不修改源代码的基础上对方法增强
         * 	分类：
         * 		基于接口的动态代理
         * 		基于子类的动态代理
         * 基于接口的动态代理：
         * 	涉及的类：Proxy
         * 	提供者：JDK官方
         * 如何创建代理对象：
         * 	使用Proxy类中的newProxyInstance方法
         * 创建代理对象的要求：
         * 	被代理类最少实现一个接口，如果没有则不能使用
         * newProxyInstance方法的参数：
         * 	ClassLoader：类加载器
         * 		用于加载代理对象字节码的，和被代理对象对象使用相同的类加载器，固定写法
         * 	Class[]：字节码数组
         * 		用于让代理对象和被代理对象有相同的方法，固定写法
         * 	InvocationHandler：用于增强的代码
         * 		用来实现如何代理的，一般都是一些接口的实现，通常使用匿名内部类。
         */
        Producer producer = (Producer) Proxy.newProxyInstance(ProducerImpl.class.getClassLoader(),
                ProducerImpl.class.getInterfaces(),
                new InvocationHandler() {
                    /**
                     * 作用：执行被代理对象的任何接口方法都会经过该方法
                     * @param proxy  代理对象的引用
                     * @param method    当前执行的方法
                     * @param args      当前执行的方法所需的参数
                     * @return      和被代理对象有相同的返回值
                     * @throws Throwable
                     */
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //提供增强的代码
                Object retuenValue = null;
                //1.获取方法执行的参数
                Float money = (Float) args[0];
                //2.判断当前方法是否为销售
                if ("saleProducer".equals(method.getName())){
                    retuenValue = method.invoke(producerImpl, money*0.8F);
                    /**
                     * 参数：指谁的方法,(指被代理对象的方法)
                     *      谁：producerImpl
                     *      方法：money*0.8F
                     *      注：匿名内部类访问外部成员变量是，外部成员要求为final修饰
                     */
                }
                if ("afterService".equals(method.getName())){
                    retuenValue = method.invoke(producerImpl, money*0.6F);
                    /**
                     * 参数：指谁的方法,(指被代理对象的方法)
                     *      谁：producerImpl
                     *      方法：money*0.8F
                     *      注：匿名内部类访问外部成员变量是，外部成员要求为final修饰
                     */
                }
                return retuenValue;

            }
        });

        producer.saleProducer(1000f);
        producer.afterService(2000F);
    }
}
